#!/bin/bash
#--------------------------------------------------
# This script is used for:
# 1. to download the scripts/binaries/images needed for installing a k8s cluster with kubeasz
# 2. to run kubeasz in a container (recommended)
# @author:   gjmzj
# @usage:    ./ezdown
# @repo:     https://github.com/easzlab/kubeasz
#--------------------------------------------------
# shellcheck disable=SC2155
set -o nounset
set -o errexit
set -o pipefail
#set -o xtrace

# default settings, can be overridden by cmd line options, see usage
DOCKER_VER=28.0.4
KUBEASZ_VER=3.6.7
K8S_BIN_VER=v1.33.1
# https://github.com/easzlab/dockerfile-kubeasz-ext-bin
EXT_BIN_VER=1.12.5
# https://github.com/easzlab/dockerfile-kubeasz-sys-pkg
SYS_PKG_VER=1.0.3
HARBOR_VER=v2.12.4
REGISTRY_MIRROR=CN

# images downloaded by default(with 'ezdown -D')
# https://github.com/projectcalico/calico
calicoVer=v3.28.4
# https://github.com/coredns/coredns
corednsVer=1.12.1
# https://kubernetes.io/docs/tasks/administer-cluster/nodelocaldns/
dnsNodeCacheVer=1.25.0
# https://github.com/kubernetes-sigs/metrics-server
metricsVer=v0.7.2
pauseVer=3.10

# images not downloaded by default(only download  with 'ezdown -X ***')
# https://github.com/cilium/cilium
# https://docs.cilium.io/en/stable/installation/k8s-install-helm/
ciliumVer=1.17.4
# https://github.com/flannel-io/flannel
flannelVer=v0.26.0
# https://github.com/cloudnativelabs/kube-router
kubeRouterVer=v1.5.4
# https://github.com/kubeovn/kube-ovn
kubeOvnVer=v1.11.5
# https://github.com/kubernetes/dashboard
dashboardVer=7.12.0
# https://github.com/rancher/local-path-provisioner
localpathProvisionerVer=v0.0.31
# https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner
nfsProvisionerVer=v4.0.2
#https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack
promChartVer=75.7.0
#https://github.com/bitnami/charts/tree/main/bitnami/kubeapps
kubeappsVer=12.4.3
#https://kubeblocks.io/docs/release-1_0/user_docs/overview/introduction
kubeblocksVer=1.0.0
#https://min.io/docs/minio/kubernetes/upstream/operations/install-deploy-manage/deploy-operator-helm.html
minioOperatorVer=7.1.1

function usage() {
  echo -e "\033[33mUsage:\033[0m ezdown [options] [args]"
  cat <<EOF
  option:
    -C         stop&clean all local containers
    -D         download default binaries/images into "$BASE"
    -P <OS>    download system packages of the OS (ubuntu_22,debian_11,...)
    -R         download Registry(harbor) offline installer
    -S         start kubeasz in a container
    -X <opt>   download extra images
    -d <ver>   set docker-ce version, default "$DOCKER_VER"
    -e <ver>   set kubeasz-ext-bin version, default "$EXT_BIN_VER"
    -k <ver>   set kubeasz-k8s-bin version, default "$K8S_BIN_VER"
    -m <str>   set docker registry mirrors, default "CN"(used in Mainland,China)
    -z <ver>   set kubeasz version, default "$KUBEASZ_VER"
EOF
}

function usage-down-sys-pkg(){
  echo -e "\033[33mUsage:\033[0m ezdown -P <OS>"
  cat <<EOF
available OSes:
    almalinux_8       to download package of AlmaLinux 8
    almalinux_9       to download package of AlmaLinux 9
    centos_7          to download package of CentOS 7
    debian_10         to download package of Debian 10
    debian_11         to download package of Debian 11
    fedora_34         to download package of Fedora 34
    fedora_35         to download package of Fedora 35
    fedora_36         to download package of Fedora 36
    fedora_37         to download package of Fedora 37
    opensuse_leap_15  to download package of openSUSE Leap 15
    rocky_8           to download package of Rocky Linux 8
    rocky_9           to download package of Rocky Linux 9
    ubuntu_16         to download package of Ubuntu 16.04
    ubuntu_18         to download package of Ubuntu 18.04
    ubuntu_20         to download package of Ubuntu 20.04
    ubuntu_22         to download package of Ubuntu 22.04
    ubuntu_24         to download package of Ubuntu 24.04
examples:
    ./ezdown -P ubuntu_22
EOF
}

function usage-down-ext-img(){
  echo -e "\033[33mUsage:\033[0m ezdown -X <opt>"
  cat <<EOF
available options:
    cilium                   to download images of cilium
    dashboard                to download images of dashboard
    flannel                  to download images of flannel
    kube-ovn                 to download images of kube-ovn
    kube-router              to download images of kube-router
    kubeapps                 to download images of kubeapps
    kubeblocks               to download images of kubeblocks
    kb-addon-mysql           to download images of kb-addon-mysql
    kb-addon-pg              to download images of kb-addon-postgresql
    kb-addon-redis           to download images of kb-addon-redis
    kb-addon-es              to download images of kb-addon-es
    kb-addon-mongodb         to download images of kb-addon-mongodb
    local-path-provisioner   to download images of local-path-provisioner
    minio                    to download images of minio
    network-check            to download images of network-check
    nfs-provisioner          to download images of nfs-provisioner
    prometheus               to download images of prometheus
examples:
    ./ezdown -X prometheus
EOF
}

function logger() {
  TIMESTAMP=$(date +'%Y-%m-%d %H:%M:%S')
  local FNAME=$(basename "${BASH_SOURCE[1]}")
  local SOURCE="\033[36m[$FNAME:${BASH_LINENO[0]}]\033[0m"
  case "$1" in
    debug)
      echo -e "\033[36m$TIMESTAMP\033[0m $SOURCE \033[36mDEBUG $2\033[0m"
      ;;
    info)
      echo -e "\033[36m$TIMESTAMP\033[0m $SOURCE \033[32mINFO $2\033[0m"
      ;;
    warn)
      echo -e "\033[36m$TIMESTAMP\033[0m $SOURCE \033[33mWARN $2\033[0m"
      ;;
    error)
      echo -e "\033[36m$TIMESTAMP\033[0m $SOURCE \033[31mERROR $2\033[0m"
      ;;
    *) ;;
  esac
}

function download_docker() {
  if [[ "$REGISTRY_MIRROR" == CN ]];then
    DOCKER_URL="https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/static/stable/${ARCH}/docker-${DOCKER_VER}.tgz"
  else
    DOCKER_URL="https://download.docker.com/linux/static/stable/${ARCH}/docker-${DOCKER_VER}.tgz"
  fi

  if [[ -f "$BASE/down/docker-${DOCKER_VER}.tgz" ]];then
    logger warn "docker binaries already existed"
  else
    logger info "downloading docker binaries, arch:$ARCH, version:$DOCKER_VER"
    if [[ -e /usr/bin/wget ]];then
      wget -c --user-agent="Mozilla" --no-check-certificate "$DOCKER_URL" || { logger error "downloading docker failed"; exit 1; }
    else
      curl -k -C- -O --retry 3 "$DOCKER_URL" || { logger error "downloading docker failed"; exit 1; }
    fi
    mv -f "./docker-$DOCKER_VER.tgz" "$BASE/down"
  fi

  tar zxf "$BASE/down/docker-$DOCKER_VER.tgz" -C "$BASE/down" && \
  mkdir -p "$BASE/bin/docker-bin" && \
  cp -f "$BASE"/down/docker/* "$BASE/bin/docker-bin" && \
  mv -f "$BASE"/down/docker/* /opt/kube/bin && \
  rm -rf "$BASE"/down/docker && \
  ln -sf /opt/kube/bin/docker /bin/docker
}

function install_docker() {
  # check if a container runtime is already installed
  systemctl status docker|grep Active|grep -q running && { logger warn "docker is already running."; return 0; }

  logger debug "generate docker service file"
  cat > /etc/systemd/system/docker.service << EOF
[Unit]
Description=Docker Application Container Engine
Documentation=http://docs.docker.io
[Service]
Environment="PATH=/opt/kube/bin:/bin:/sbin:/usr/bin:/usr/sbin"
ExecStart=/opt/kube/bin/dockerd
ExecStartPost=/sbin/iptables -I FORWARD -s 0.0.0.0/0 -j ACCEPT
ExecReload=/bin/kill -s HUP \$MAINPID
Restart=on-failure
RestartSec=5
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
Delegate=yes
KillMode=process
[Install]
WantedBy=multi-user.target
EOF

  # configuration for dockerd
  mkdir -p /etc/docker
  DOCKER_VER_MAIN=$(echo "$DOCKER_VER"|cut -d. -f1)
  CGROUP_DRIVER="cgroupfs"
  ((DOCKER_VER_MAIN>=20)) && CGROUP_DRIVER="systemd"
  logger debug "generate docker config: /etc/docker/daemon.json"
  if [[ "$REGISTRY_MIRROR" == CN ]];then
    logger debug "prepare register mirror for $REGISTRY_MIRROR"
    cat > /etc/docker/daemon.json << EOF
{
  "exec-opts": ["native.cgroupdriver=$CGROUP_DRIVER"],
  "registry-mirrors": [
    "https://docker.1ms.run",
    "https://hub1.nat.tf",
    "https://docker.1panel.live",
    "https://proxy.1panel.live",
    "https://hub.rat.dev",
    "https://docker.amingg.com"
  ],
  "insecure-registries": ["http://easzlab.io.local:5000"],
  "max-concurrent-downloads": 10,
  "log-driver": "json-file",
  "log-level": "warn",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
    },
  "data-root": "/var/lib/docker"
}
EOF
  else
    logger debug "standard config without registry mirrors"
    cat > /etc/docker/daemon.json << EOF
{
  "exec-opts": ["native.cgroupdriver=$CGROUP_DRIVER"],
  "insecure-registries": ["http://easzlab.io.local:5000"],
  "max-concurrent-downloads": 10,
  "log-driver": "json-file",
  "log-level": "warn",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
    },
  "data-root": "/var/lib/docker"
}
EOF
  fi

  if [[ -f /etc/selinux/config ]]; then
    logger debug "turn off selinux"
    getenforce|grep Disabled || setenforce 0
    sed -i 's/^SELINUX=.*$/SELINUX=disabled/g' /etc/selinux/config
  fi

  logger debug "enable and start docker"
  systemctl enable docker
  systemctl daemon-reload && systemctl restart docker && sleep 3
}

function get_kubeasz() {
  # check if kubeasz already existed
  [[ -d "$BASE/roles/kube-node" ]] && { logger warn "kubeasz already existed"; return 0; }

  if [[ ! -f "$imageDir/kubeasz_$KUBEASZ_VER.tar" ]];then
    logger info "downloading kubeasz: $KUBEASZ_VER"
    docker pull "easzlab/kubeasz:$KUBEASZ_VER" && \
    docker save -o "$imageDir/kubeasz_$KUBEASZ_VER.tar" "easzlab/kubeasz:$KUBEASZ_VER" || \
    { logger error "download failed!"; return 1; }
  else
    docker load -i "$imageDir/kubeasz_$KUBEASZ_VER.tar"
  fi

  docker ps -a |grep -q temp_easz && { logger debug "remove existing container"; docker rm -f temp_easz; }
  logger debug " run a temporary container"
  docker run -d --name temp_easz easzlab/kubeasz:${KUBEASZ_VER} || { logger error "failed."; exit 1; }

  [[ -d "$BASE/down" ]] && /bin/mv -f "$BASE/down" /tmp
  [[ -d "$BASE/bin" ]] && /bin/mv -f "$BASE/bin" /tmp

  rm -rf "$BASE" && \
  logger debug "cp kubeasz code from the temporary container" && \
  docker cp "temp_easz:$BASE" "$BASE" && \
  logger debug "stop&remove temporary container" && \
  docker rm -f temp_easz

  mkdir -p "$BASE/bin" "$BASE/down"
  [[ -d "/tmp/down" ]] && /bin/mv -f /tmp/down/* "$BASE/down"
  [[ -d "/tmp/bin" ]] && /bin/mv -f /tmp/bin/* "$BASE/bin"
  return 0
}

function get_k8s_bin() {
  [[ -f "$BASE/bin/kubelet" ]] && { logger warn "kubernetes binaries existed"; return 0; }

  logger info "downloading kubernetes: $K8S_BIN_VER binaries"
  rm -rf "$BASE/k8s_bin_tmp"
  docker ps -a |grep -q temp_k8s_bin && { logger debug "remove existing container"; docker rm -f temp_k8s_bin; }
  docker pull easzlab/kubeasz-k8s-bin:"$K8S_BIN_VER" && \
  logger debug "run a temporary container" && \
  docker run -d --name temp_k8s_bin easzlab/kubeasz-k8s-bin:${K8S_BIN_VER} && \
  logger debug "cp k8s binaries" && \
  docker cp temp_k8s_bin:/k8s "$BASE/k8s_bin_tmp" && \
  /bin/mv -f "$BASE"/k8s_bin_tmp/* "$BASE/bin" && \
  logger debug "stop&remove temporary container" && \
  docker rm -f temp_k8s_bin && \
  rm -rf "$BASE/k8s_bin_tmp"
}

function get_ext_bin() {
  [[ -f "$BASE/bin/etcdctl" ]] && { logger warn "extra binaries existed"; return 0; }

  logger info "downloading extral binaries kubeasz-ext-bin:$EXT_BIN_VER"
  rm -rf "$BASE/extra_bin_tmp"
  docker ps -a |grep -q temp_ext_bin && { logger debug "remove existing container"; docker rm -f temp_ext_bin; }
  docker pull "easzlab/kubeasz-ext-bin:$EXT_BIN_VER" && \
  logger debug "run a temporary container" && \
  docker run -d --name temp_ext_bin "easzlab/kubeasz-ext-bin:$EXT_BIN_VER" && \
  logger debug "cp extral binaries" && \
  docker cp temp_ext_bin:/extra "$BASE/extra_bin_tmp" && \
  /bin/mv -f "$BASE"/extra_bin_tmp/* "$BASE/bin" && \
  logger debug "stop&remove temporary container" && \
  docker rm -f temp_ext_bin && \
  rm -rf "$BASE/extra_bin_tmp"
}

function get_sys_pkg() {
  [[ -f "$BASE/down/packages/$1.tgz" ]] && { logger warn "system packages for $1 existed"; return 0; }

  docker ps -a |grep -q temp_sys_pkg && { logger debug "remove existing container"; docker rm -f temp_sys_pkg; }
  logger info "downloading system packages kubeasz-sys-pkg:$SYS_PKG_VER"
  docker pull "easzlab/kubeasz-sys-pkg:$SYS_PKG_VER" && \
  logger debug "run a temporary container" && \
  docker run -d --name temp_sys_pkg "easzlab/kubeasz-sys-pkg:$SYS_PKG_VER" && \
  logger debug "cp system packages" && \
  docker cp temp_sys_pkg:/packages "$BASE/down" && \
  logger debug "stop&remove temporary container" && \
  docker rm -f temp_sys_pkg
}

function get_harbor_offline_pkg() {
  [[ -f "$BASE/down/harbor-offline-installer-$HARBOR_VER.tgz" ]] && { logger warn "harbor-offline existed"; return 0; }

  docker ps -a |grep -q temp_harbor && { logger debug "remove existing container"; docker rm -f temp_harbor; }
  logger info "downloading harbor-offline:$HARBOR_VER"
  docker pull "easzlab/harbor-offline:$HARBOR_VER" && \
  logger debug "run a temporary container" && \
  docker run -d --name temp_harbor "easzlab/harbor-offline:$HARBOR_VER" && \
  logger debug "cp harbor-offline installer package" && \
  docker cp "temp_harbor:/harbor-offline-installer-$HARBOR_VER.tgz" "$BASE/down" && \
  logger debug "stop&remove temporary container" && \
  docker rm -f temp_harbor
}

function get_default_images() {
  logger info "download default images, then upload to the local registry"

  IMAGES=(\
      "calico/cni:$calicoVer" \
      "calico/kube-controllers:$calicoVer" \
      "calico/node:$calicoVer" \
      "coredns/coredns:$corednsVer" \
      "easzlab/k8s-dns-node-cache:$dnsNodeCacheVer" \
      "easzlab/metrics-server:$metricsVer" \
      "easzlab/pause:$pauseVer" \
    )
  down_and_save_images
}

function get_extra_images() {
  logger info "download images for $1, then upload to the local registry"

  case "$1" in
    cilium)
      IMAGES=(\
          "cilium/cilium:v$ciliumVer" \
          "cilium/operator-generic:v$ciliumVer" \
          "cilium/hubble-relay:v$ciliumVer" \
          "cilium/hubble-ui-backend:v0.13.2" \
          "cilium/hubble-ui:v0.13.2" \
        )
      down_and_save_images cilium
      ;;

    flannel)
      IMAGES=(\
          "flannel/flannel:$flannelVer" \
          "flannel/flannel-cni-plugin:v1.5.1-flannel2" \
        )
      down_and_save_images flannel
      ;;

    dashboard)
      IMAGES=(\
          "kubernetesui/dashboard-api:1.12.0" \
          "kubernetesui/dashboard-auth:1.2.4" \
          "kubernetesui/dashboard-metrics-scraper:1.2.2" \
          "kubernetesui/dashboard-web:1.6.2" \
          "kong:3.8" \
        )
      down_and_save_images kubernetesui
      ;;

    kubeapps)
      IMAGES=(\
          "bitnami/kubeapps-apis:2.7.0-debian-11-r10" \
          "bitnami/kubeapps-apprepository-controller:2.7.0-scratch-r0" \
          "bitnami/kubeapps-asset-syncer:2.7.0-scratch-r0" \
          "bitnami/kubeapps-dashboard:2.7.0-debian-11-r12" \
          "bitnami/nginx:1.23.4-debian-11-r18" \
          "bitnami/postgresql:15.3.0-debian-11-r0" \
        )
      down_and_save_images bitnami
      ;;

    kubeblocks)
      IMAGES=(\
          "swr.cn-north-4.myhuaweicloud.com/ddn-k8s/registry.k8s.io/sig-storage/snapshot-controller:v8.3.0" \
          "apecloud-registry.cn-zhangjiakou.cr.aliyuncs.com/apecloud/kubeblocks-charts:${kubeblocksVer}" \
          "apecloud-registry.cn-zhangjiakou.cr.aliyuncs.com/apecloud/kubeblocks:${kubeblocksVer}" \
          "apecloud-registry.cn-zhangjiakou.cr.aliyuncs.com/apecloud/kubeblocks-tools:${kubeblocksVer}" \
          "apecloud-registry.cn-zhangjiakou.cr.aliyuncs.com/apecloud/kubeblocks-dataprotection:${kubeblocksVer}" \
          "apecloud-registry.cn-zhangjiakou.cr.aliyuncs.com/apecloud/datasafed:0.2.0" \
        )
      down_and_save_images apecloud
      ;;

    kb-addon-mysql)
      IMAGES=(\
          "apecloud/mysql_audit_log:8.0.33" \
          "apecloud/xtrabackup:8.0" \
          "apecloud/jemalloc:5.3.0" \
          "apecloud/syncer:0.5.0" \
          "apecloud/mysql:8.0.39" \
          "apecloud/mysqld-exporter:0.15.1" \
          "apecloud/proxysql:2.4.4" \
          "apecloud/percona-xtrabackup:8.0" \
          "apecloud/wal-g-mysql:2.0.1-1-ubuntu" \
        )
      down_and_save_images apecloud
      ;;

    kb-addon-pg)
      IMAGES=(\
          "apecloud/spilo:16.4.0" \
          "apecloud/dbctl:0.1.7" \
          "apecloud/pgbouncer:1.19.0" \
          "apecloud/postgres-exporter:v0.15.0" \
        )
      down_and_save_images apecloud
      ;;

    kb-addon-redis)
      IMAGES=(\
          "apecloud/dbctl:0.1.7" \
          "apecloud/agamotto:0.1.2-beta.1" \
          "apecloud/redis-stack-server:7.2.0-v14" \
        )
      down_and_save_images apecloud
      ;;

    kb-addon-mongodb)
      IMAGES=(\
          "apecloud/syncer:0.3.7" \
          "apecloud/mongo:5.0.30" \
        )
      down_and_save_images apecloud
      ;;

    kb-addon-es)
      IMAGES=(\
          "apecloud/kibana:8.8.2" \
          "apecloud/elasticsearch-plugins:8.8.2" \
          "apecloud/elasticsearch:8.8.2" \
          "apecloud/elasticsearch-exporter:v1.7.0" \
          "apecloud/curl-jq:0.1.0" \
        )
      down_and_save_images apecloud
      ;;

    kube-ovn)
      IMAGES=(\
          "kubeovn/kube-ovn:$kubeOvnVer" \
        )
      down_and_save_images kubeovn
      ;;

    kube-router)
      IMAGES=(\
          "cloudnativelabs/kube-router:$kubeRouterVer" \
        )
      down_and_save_images cloudnativelabs
      ;;

    local-path-provisioner)
      IMAGES=(\
          "rancher/local-path-provisioner:$localpathProvisionerVer" \
        )
      down_and_save_images rancher
      ;;

    minio)
      IMAGES=(\
          "quay.io/minio/operator:v${minioOperatorVer}" \
          "quay.io/minio/operator-sidecar:v7.0.1" \
          "quay.io/minio/minio:RELEASE.2025-04-08T15-41-24Z" \
        )
      down_and_save_images minio
      ;;

    network-check)
      IMAGES=(\
          "easzlab/json-mock:v1.3.0" \
          "easzlab/alpine-curl:v7.85.0" \
        )
      down_and_save_images
      ;;

    nfs-provisioner)
      IMAGES=(\
          "easzlab/nfs-subdir-external-provisioner:$nfsProvisionerVer" \
        )
      down_and_save_images
      ;;

    prometheus)
      IMAGES=(\
          "easzlab/kube-state-metrics:v2.16.0" \
          "easzlab/kube-webhook-certgen:v1.6.0" \
          "grafana/grafana:12.0.2" \
          "quay.io/kiwigrid/k8s-sidecar:1.30.5" \
          "quay.io/prometheus-operator/prometheus-config-reloader:v0.83.0" \
          "quay.io/prometheus-operator/prometheus-operator:v0.83.0" \
          "quay.io/prometheus/alertmanager:v0.28.1" \
          "quay.io/prometheus/node-exporter:v1.9.1" \
          "quay.io/prometheus/prometheus:v3.4.2" \
        )
      down_and_save_images prometheus
      ;;

    *)
      logger error "invalid option: $1"
      usage-down-ext-img
      exit 1
      ;;
  esac
}


function down_and_save_images(){
  NS="easzlab"
  [ "$#" -eq 1 ] && NS="$1"
  for item in "${IMAGES[@]}"; do
    image_part="${item##*/}"
    image_name="${image_part%:*}"
    image_tag="${image_part##*:}"
    image_file="$imageDir/${image_name}_${image_tag}.tar"
    if [[ ! -f "$image_file" ]];then
      docker pull "$item" && \
      docker save -o "$image_file" "$item" || \
      { logger error "download $item failed!"; return 1; }
    else
      docker load -i "$image_file"
    fi
    docker tag "$item" "easzlab.io.local:5000/${NS}/${image_part}"
    docker push "easzlab.io.local:5000/${NS}/${image_part}" || \
    { logger error "push easzlab.io.local:5000/${NS}/${image_part} failed!"; return 1; }
  done
}


function download_all() {
  mkdir -p /opt/kube/bin "$BASE/down" "$BASE/bin"
  download_docker && \
  install_docker && \
  get_kubeasz && \
  get_k8s_bin && \
  get_ext_bin && \
  start_local_registry && \
  get_default_images
}

function start_local_registry() {
  docker ps -a --format="{{ .Names }}"|grep local_registry > /dev/null 2>&1 && \
  { logger warn "local_registry is already running"; return 0; }

  if [[ ! -f "$imageDir/registry-2.tar" ]];then
    docker pull "registry:2" && \
    docker save -o "$imageDir/registry-2.tar" "registry:2"
  fi

  logger info "start local registry ..."
  docker load -i "$imageDir/registry-2.tar" > /dev/null
  mkdir -p /opt/kube/registry
  docker run -d \
        --name local_registry \
        --network host \
        --restart always \
        --volume /opt/kube/registry:/var/lib/registry \
        registry:2

  sed -i "/easzlab.io.local/d" /etc/hosts
  echo "127.0.0.1  easzlab.io.local" >> /etc/hosts
}


function start_kubeasz_docker() {
  # create cmd alias in /root/.bashrc
  sed -i '/docker exec/d' /root/.bashrc
  echo "alias dk='docker exec -it kubeasz'  # generated by kubeasz" >> /root/.bashrc

  [[ -d "$BASE/roles/kube-node" ]] || { logger error "not initialized. try 'ezdown -D' first."; exit 1; }
  docker ps -a --format="{{ .Names }}"|grep kubeasz > /dev/null 2>&1 && \
  docker rm -f kubeasz > /dev/null

  if [[ ! -f "$imageDir/kubeasz_$KUBEASZ_VER.tar" ]];then
    logger info "downloading kubeasz: $KUBEASZ_VER"
    docker pull "easzlab/kubeasz:$KUBEASZ_VER" && \
    docker save -o "$imageDir/kubeasz_$KUBEASZ_VER.tar" "easzlab/kubeasz:$KUBEASZ_VER"
  else
    docker load -i "$imageDir/kubeasz_$KUBEASZ_VER.tar"
  fi

  logger info "try to run kubeasz in a container"
  # get host's IP
  host_if=$(ip route|grep default|head -n1|cut -d' ' -f5)
  host_ip=$(ip a|grep "$host_if$"|head -n1|awk '{print $2}'|cut -d'/' -f1)
  logger debug "get host IP: $host_ip"

  # allow ssh login using key locally
  if [[ ! -e /root/.ssh/id_rsa ]]; then
    logger debug "generate ssh key pair"
    ssh-keygen -t rsa -b 2048 -N '' -f /root/.ssh/id_rsa > /dev/null
    cat /root/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys
    ssh-keyscan -t ecdsa -H "$host_ip" >> /root/.ssh/known_hosts
  fi

  # run kubeasz docker container
  docker run --detach \
      --env HOST_IP="$host_ip" \
      --name kubeasz \
      --network host \
      --restart always \
      --volume "$BASE":"$BASE" \
      --volume /root/.kube:/root/.kube \
      --volume /root/.ssh:/root/.ssh \
      --volume /etc/docker:/etc/docker \
      easzlab/kubeasz:${KUBEASZ_VER}
}

function clean_container() {
 logger info "clean all running containers"
 docker ps -a|awk 'NR>1{print $1}'|xargs docker rm -f
}


### Main Lines ##################################################
function main() {
  BASE="/etc/kubeasz"
  IMAGES=()
  imageDir="$BASE/down"

  # check if use bash shell
  # readlink /proc/$$/exe|grep -q "bash" || { logger error "you should use bash shell, not sh"; exit 1; }
  # check if use with root
  # [[ "$EUID" -ne 0 ]] && { logger error "you should run this script as root"; exit 1; }

  # get architecture
  ARCH=$(uname -m)

  [[ "$#" -eq 0 ]] && { usage >&2; exit 1; }

  ACTION=""
  while getopts "CDP:RSX:d:e:k:m:z:" OPTION; do
      case "$OPTION" in
        C)
          ACTION="clean_container"
          ;;
        D)
          ACTION="download_all"
          ;;
        P)
          [[ $OPTARG =~ (ubuntu_[0-9]+|centos_[0-9]+|debian_[0-9]+|fedora_[0-9]+|almalinux_[0-9]+|opensuse_leap_[0-9]+|rocky_[0-9]+) ]] || \
          { usage-down-sys-pkg; exit 1; }
          SYS_PKG_VER="${SYS_PKG_VER}_$OPTARG"
          ACTION="get_sys_pkg $OPTARG"
          ;;
        R)
          ACTION="get_harbor_offline_pkg"
          ;;
        S)
          ACTION="start_kubeasz_docker"
          ;;
        X)
          ACTION="get_extra_images $OPTARG"
          ;;
        d)
          DOCKER_VER="$OPTARG"
          ;;
        e)
          EXT_BIN_VER="$OPTARG"
          ;;
        k)
          K8S_BIN_VER="$OPTARG"
          ;;
        m)
          REGISTRY_MIRROR="$OPTARG"
          ;;
        z)
          KUBEASZ_VER="$OPTARG"
          ;;
        ?)
          usage
          exit 1
          ;;
      esac
  done

  [[ "$ACTION" == "" ]] && { logger error "illegal option"; usage; exit 1; }

  # excute cmd "$ACTION"
  logger info "Action begin: $ACTION"
  ${ACTION} || { logger error "Action failed: $ACTION"; return 1; }
  logger info "Action successed: $ACTION"
}

main "$@"
